package com.xish.spring.security;

import com.xish.spring.env.Env;
import com.xish.util.MD5;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

@Component
public class CheckSignatureInterceptor extends HandlerInterceptorAdapter {

    private static final Logger LOG = LoggerFactory.getLogger(CheckSignatureInterceptor.class);

    @Autowired
    private Env env;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        LOG.info("CheckSignatureInterceptor.preHandle, uri={}, handler={}", request.getRequestURI(), handler);

        if ((handler == null) || !(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        CheckSignature annotation = handlerMethod.getMethodAnnotation(CheckSignature.class);
        if (annotation == null) {
            LOG.info("no CheckSignature annotation, pass.");
            return true;
        }

        LOG.info("begin to check signature");

        String signature = request.getHeader("HTTP_X_REST_APPSIGN");
        if (signature == null) {
            LOG.warn("request header [HTTP_X_REST_APPSIGN] not exists.");
            throw new SignatureException();
        } else {
            signature = signature.toLowerCase();
        }

        String appKey = "HTTP_X_REST_APPKEY";
        String appKeyValue = request.getHeader(appKey);
        if (appKeyValue == null || appKeyValue.isEmpty()) {
            LOG.warn("request header [HTTP_X_REST_APPKEY] not exists.");
            throw new SignatureException();
        }

        List<String> keys = new ArrayList<>();
        keys.add("app_key"); // appKey, HTTP_X_REST_APPKEY

        Enumeration<String> paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            LOG.debug("Parameter = {}", paramName);

            keys.add(paramName);
        }

        StringBuilder builder = new StringBuilder();
        Collections.sort(keys);
        for (String key : keys) {
            if (key.equals("app_key")) {
                builder.append(key).append('=').append(appKeyValue).append('&');
            } else {
                builder.append(key).append('=').append(request.getParameter(key)).append('&');
            }
        }

        String appSecret = env.get("app.secret");
        LOG.info("appSecret = {}", appSecret);
        builder.append(appSecret);

        String calculatedSignature = MD5.calculate(builder.toString()).toLowerCase();

        if (!signature.equals(calculatedSignature)) {
            LOG.warn("signature not match, expect={}, actually={}", signature, calculatedSignature);
            throw new SignatureException();
        }

        return true;
    }
}
